import zipfile
import re
from StringIO import StringIO
import xml.etree.ElementTree as etree
import base64
import mimetypes
mtypes = mimetypes.read_mime_types('mime.types')
for key in mtypes.keys():
    mimetypes.add_type(mtypes[key], key)

mimetypes.init()
class epub():
    def __init__(self, epubfile):
        self.myzip = zipfile.ZipFile(epubfile)

    def resolve_path(self, path, referrer_location):
        path_dirs = path.split("/")
        file_name = path_dirs.pop()
        location_dirs = referrer_location.split("/")
        location_dirs.pop()
        for spec in path_dirs:
            if spec == "..": location_dirs.pop()
            else: location_dirs.append(spec)
        location_dirs.append(file_name)
        return '/'.join(location_dirs)

    def get_data_uri(self, path, referrer_location=False):
        if referrer_location: data_href = self.self.resolve_path(path, referrer_location)
        else: data_href = path
        mimetype = mimetypes.guess_type(path)
        print path, mimetype
        return "data:"+mimetype[0]+","+base64.b64encode(self.myzip.read(opfname))

    def process_css(self, csspath):
        reurl = re.compile("url\((.*?)\)", re.I|re.M)
        csstext = self.myzip.read(csspath).replace('body', '#epubcont')
        def fformats(path):
            formats = {'.otf':' format(\'opentype\')',\
                       '.ttf':' format(\'truetype\')',\
                       '.eot':' format(\'embedded-opentype\')',\
                       'woff':' format(\'woff\')'}
            ext = path[-4:]
            if ext in formats.keys(): return formats[ext]
            else: return ''

        return reurl.sub(lambda x: 'url('+self.get_data_uri(x.group(1), csspath)+')'+fformats(x.group(1)), csstext)


    def process_html(self, mpath):
        #Images
        mhtml = etree.fromstring(self.myzip.read(mpath))
        images = [a for a in mhtml.iter() if re.match('(.*\})?(image|img)$', a.tag)]
        for image in images:
            print image.attrib
            print self.resolve_path(image.attrib['src'], mpath)
            image.attrib['src'] = self.get_data_uri(image.attrib['src'], mpath)
        #CSS
        linkscss  = [a for a in mhtml.iter() if re.match('(.*\})?(link)$', a.tag) and a.attrib['type']=='text/css']
        for linkcss in linkscss:
            csspath = self.resolve_path(linkcss.attrib['href'], mpath)
            style = etree.SubElement(mhtml, 'style')
            style.attrib['type'] = 'text/css'
            print csspath
            style.text = '\n'+self.process_css(csspath)+'\n'
        div = etree.Element(tag='div', attrib={'id':'epubcont'})
        div.append(mhtml)
        return mhtml

    def get_toc_href(self, opf):
        #keyre = re.compile('^(ncx|toc)$', re.I)
        old_tocre = re.compile('.+?\.ncx', re.I)
        new_tocre = re.compile('.*?(toc\.xhtml|nav\.xhtml)', re.I)
        tocs = []
        oldtocs = []
        tochref = ''
        for key in opf['manifest'].keys():
            media_type = opf['manifest'][key]['media-type']
            href = opf['manifest'][key]['href']
            if key=='ncx' or old_tocre.match(href) or media_type == 'application/x-dtbncx+xml':
                print "Old toc is:", key, opf['manifest'][key]
                oldtocs.append(href)
            elif media_type == 'application/x-dtbncx+xml' or new_tocre.match(href) or key=='toc':
                print "New toc is:", key, opf['manifest'][key]
                tocs.append(href)
        if len(tocs): 
            if len(tocs)==1: return (tocs[0], 'new',)
            else: return (filter(lambda x: not 'short' in x, tocs)[0], 'new',)
        else: return (oldtocs[0], 'old',)
        #return etree.fromstring(self.myzip.read(href))


    def parsencx(self, base):
        ncx = etree.fromstring(self.myzip.read(base))
        points = []
        doc_title = [a for a in ncx.iter() if re.match('(.*\})?(docTitle)$', a.tag)][0]
        title = [a for a in doc_title.iter() if re.match('(.*\})?(text)$', a.tag)][0].text
        navPoints = [a for a in ncx.iter() if re.match('(.*\})?(navPoint)$', a.tag)]
        for navPoint in navPoints:
            point = {}
            point['src'] = self.resolve_path([a for a in navPoint.iter() if \
                                              re.match('(.*\})?(content)$', a.tag)][0].attrib['src'], base)
            point['name'] = [a for a in navPoint.iter() if re.match('(.*\})?(text)$', a.tag)][0].text
            point['order'] = int(navPoint.attrib['playOrder'])
            points.append(point)
        points.sort(key=lambda x: x['order'])
        return (title, points,)

    def parsetochtml(self, base):
        tochtml = etree.fromstring(self.myzip.read(base))
        points = []
        head = [a for a in tochtml.iter() if re.match('(.*\})?(head)$', a.tag)][0]
        title = [a for a in head.iter() if re.match('(.*\})?(title)$', a.tag)][0].text
        print title
        nav = [a for a in tochtml.iter() if re.match('(.*\})?(nav)$', a.tag)][0]
        lis = [a for a in nav.iter() if re.match('(.*\})?(li)$', a.tag)]
        for li in lis:
            point = {}
            a = [a for a in li.iter() if re.match('(.*\})?(a)$', a.tag)][0]
            point['src'] = self.resolve_path(a.attrib['href'], base)
            point['name'] = a.text
            points.append(point)
        return (title, points,)

    def selectfromtoc(self, toc):
        sel = etree.Element(tag='select', attrib={'id':'tocselect'})
        i = 0
        for el in toc:
            option = etree.Element(tag='option', attrib={'id':str(i), 'url':el['src']}) 
            option.text = el['name']
            sel.append(option)
            i+=1
        return sel
    def process_epub(self):
        #myzip.namelist()
        containerstr = self.myzip.open('META-INF/container.xml').read()
        container = etree.fromstring(containerstr)
        opfname = [a for a in container.iter() if re.match('.*rootfile$', a.tag)][0].attrib['full-path']
        print opfname
        opftext = self.myzip.read(opfname)#.read()
        opf = {'metadata': {}, 'manifest': {}, 'spine': []}
        opfxml = etree.fromstring(opftext)
        opfitems = [a for a in opfxml.iter() if re.match('.*item$', a.tag)]
        for item in opfitems:
            opf['manifest'][item.attrib['id']] = {'href': self.resolve_path(item.attrib['href'], opfname),\
                                                  'media-type': item.attrib['media-type']}
        spine = [a for a in opfxml.iter() if re.match('.*spine$', a.tag)][0]
        spineitems = [a for a in spine.iter() if re.match('.*itemref$', a.tag)]
        for spineitem in spineitems:
            opf['spine'].append(spineitem.attrib['idref'])
        etree.register_namespace('', 'http://www.w3.org/1999/xhtml')
        #mhtml = self.process_html()
        tocxml = self.get_toc_href(opf)
        if tocxml[1] == 'old':
            toc = self.parsencx(tocxml[0])
        elif tocs[1] == 'new':
            toc = self.parsetochtml(tocxml[0])
        self.sel = self.selectfromtoc(toc[1])
        print self.sel
